home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Network Supervisor's Toolkit
/
Network Supervisor's Toolkit.iso
/
perform
/
netframe
/
dx.c
next >
Wrap
C/C++ Source or Header
|
1996-07-10
|
20KB
|
707 lines
/*
$Module DX.C$
Copyright 1990
By NetFRAME Systems Inc.
Sunnyvale, California U.S.A.
$Author: Karl S. Johnson $
$Date: 02 Feb 1990 15:46:42 $
$Revision: 1.2 $
$Description$
This is a NetWare Loadable Module to measure disk performance.
$EndDescription$
Revision History
$Log: H:/386/NLMS/DX/SRC/VCS/DX.C $
*
* Rev 1.2 02 Feb 1990 15:46:42 Karl S. Johnson
* Changed to use PDiskRequest to allow greater than cache block size I/Os.
*
* Rev 1.1 01 Feb 1990 14:47:18 Karl S. Johnson
* Added full random I/O. Added paged display and processor utilization.
*
* Rev 1.0 01 Feb 1990 10:03:50 Karl S. Johnson
* Initial revision.
*/
#include "procdefs.h"
#include "dmanage.h"
#include "random.h"
#define DX_PRIORITY 50
#define MAX_DISKS 64
#define MAX_REQUESTS_PER_DISK 64
#define MAX_KB_PER_IO 60
#define DISKS_PER_PAGE 16
#define STACK_SIZE 2048*16
#define _UNUSED(x) if (0) if (x)
extern LONG NumberOfPollingLoops, MaximumNumberOfPollingLoops;
BYTE *DXRequestArea;
LONG DXRequestAreaSize;
struct rstruct *DXRequestFreeListHead;
struct rstruct *DXRequestFreeListTail;
LONG DXUpdateInterval;
LONG DXKBytesPerIO;
LONG DXMaxIOsPerDrive;
BYTE DXReadWrite;
BYTE DXAccessType;
ScreenStruct *dXerciseScreen;
int DriveCount;
BYTE *stack; /* DX Monitor Stack */
LONG stackSize;
BYTE *DXStack = NULL; /* DX Exerciser Stack */
LONG DXActualStackSize;
LONG dXerciseModuleHandle;
LONG ServerProcessPriority = 50;
LONG DXMonitorProcessID = 0;
LONG DXerciseProcessID = 0;
int DXWakeRequested = 0;
char DXMonitorStatus = 'I'; /* Possible Status I=Initializing R=Running P=Pending Stop S=Stopped */
struct DiskData
{
struct ldstruct *LDisk;
struct plock *Lock;
int OutstandingIOs;
LONG Size;
LONG Sector;
LONG Random;
BYTE Name[80];
} Disk[MAX_DISKS];
struct PerformanceData
{
LONG TotalIOsCompleted;
LONG CurrentIOsCompleted;
LONG TotalErrors;
} Performance[MAX_DISKS];
struct PerformanceData Aggregate;
int AggregateOutstandingIOs = 0;
BYTE CommonBuffer[1024*MAX_KB_PER_IO];
void
LockAlert( LONG skipParameter, LONG criticality, int DriveIndex );
void
DXIOCompletion( struct rstruct *currentRequest );
struct rstruct *DXAllocateRequest();
void
DXReturnRequest( struct rstruct *Request);
void
MakeDriveDescription( struct pdstruct *driveID, BYTE *buffer);
void
DXercise();
void
DXMonitor();
LONG
StartProcedure(
LONG moduleHandle,
ScreenStruct *initializationErrorScreen,
BYTE *commandLine,
BYTE *loadDirectoryPath,
LONG unitializedDataLength,
LONG fileHandle,
LONG (*ReadRoutine)(LONG handle, LONG offset, BYTE *buffer, LONG length),
LONG customDataOffset,
LONG customDataSize)
{
static BYTE dXerciseScreenName[] = "DX Screen";
BYTE *Token;
_UNUSED(commandLine);
_UNUSED(loadDirectoryPath);
_UNUSED(unitializedDataLength);
_UNUSED(fileHandle);
_UNUSED(ReadRoutine);
_UNUSED(customDataOffset);
_UNUSED(customDataSize);
for (Token = commandLine; *Token != '\000'; Token++)
{
if (*Token == 'D') EnterDebugger();
}
stack = GetNonMovableMemory(STACK_SIZE, &stackSize);
if (stack == NULL)
{
OutputToScreen(initializationErrorScreen,
"DX: Unable to get memory for stack\r\n");
goto Error0;
}
dXerciseModuleHandle = moduleHandle;
if (OpenScreen(dXerciseScreenName, &dXerciseScreen) != 0)
{
OutputToScreen(initializationErrorScreen,
"DX: Unable to open DX screen\r\n");
goto Error1;
}
/* The variable stackSize contains the amount of memory actually
allocated for the stack, so use it instead of STACK_SIZE.*/
/* this should be the last thing we do */
DXMonitorProcessID = CCreateProcess(DX_PRIORITY, DXMonitor,
stack + stackSize, stackSize, "DXercise");
while ( DXMonitorStatus == 'I') CRescheduleLast();
switch ( DXMonitorStatus)
{
case 'R':
break;
case 'S':
default:
OutputToScreen( systemConsoleScreen,
"Failed to start DX monitor process\r\n" );
goto Error3;
}
return (0);
/* Error Recovery */
Error3:
CloseScreen(dXerciseScreen);
Error1:
ReturnNonMovableMemory(stack);
Error0:
return (-1);
}
void ExitProcedure(void)
{
int i;
if ( DXMonitorProcessID != 0 ) CDestroyProcess( DXMonitorProcessID );
if ( DXMonitorStatus != 'S' ) /* if not already stopped - stop */
{
DXMonitorStatus = 'S';
while ( AggregateOutstandingIOs != 0 ) CRescheduleLast();
if ( DXerciseProcessID != 0 ) CDestroyProcess( DXerciseProcessID );
}
for ( i = 0; i < DriveCount; i++ ) ReleasePartition( Disk[i].Lock );
CloseScreen( dXerciseScreen );
if ( DXStack != NULL ) ReturnNonMovableMemory( DXStack );
if ( DXRequestArea != NULL ) ReturnNonMovableMemory( DXRequestArea );
ReturnNonMovableMemory( stack );
}
void DXExit(void)
{
KillMe((struct LoadDefinitionStructure *)dXerciseModuleHandle);
/* Sleep forever until the exit procedure kills this process */
for (;;)
CSleepUntilInterrupt();
}
void
DXMonitor()
{
struct pdstruct *driveID;
struct ldstruct *lDisk;
long int LStatus;
int DriveIndex;
LONG MaxPage;
LONG CurrentPage;
LONG FirstDrive;
LONG LastDrive;
LONG StartTime;
LONG IntervalStartTime;
LONG ElapsedSeconds;
LONG IntervalSeconds;
LONG Tenths;
LONG utilization;
char *AccessString;
char *IOTypeString;
struct rstruct *Request;
LONG UpdateTicks;
int i;
BYTE Answer;
BYTE Dummy;
BYTE buffer[200];
RandomSectorsCount = sizeof( RandomSectors ) / 4;
/* Activate our screen */
Enable();
ActivateScreen(dXerciseScreen);
/* Tell DXLoad we are Running */
DXMonitorStatus = 'R';
/* Ask for type of disk drive access S=Sequential R=Random*/
InputFromScreen( dXerciseScreen,
"FSR",
2,
2,
buffer,
0L,
TRUE,
"S",
"Exerciser access pattern [S=Sequential R=Random]? " );
DXAccessType = buffer[0];
switch ( DXAccessType )
{
case 'F':
AccessString = "Fixed";
break;
case 'S':
AccessString = "Sequential";
break;
case 'R':
AccessString = "Random";
break;
default:
AccessString = "UnknownTest";
}
/* Ask for Read or Write test */
InputFromScreen( dXerciseScreen,
"RW",
2,
2,
buffer,
0L,
TRUE,
"R",
"Exerciser test type [R=Read W=Write]? " );
DXReadWrite = buffer[0];
switch ( DXReadWrite )
{
case 'R':
IOTypeString = "Read";
break;
case 'W':
IOTypeString = "Write";
break;
default:
IOTypeString = "UnknownIO";
}
/* Ask for block size */
PromptForUnsignedNumber( dXerciseScreen,
&DXKBytesPerIO,
1L,
MAX_KB_PER_IO,
10L,
0L,
TRUE,
8L,
"Number of kilo bytes (KB) per IO [1-%d]? ",
MAX_KB_PER_IO);
/* Ask for queue depth */
PromptForUnsignedNumber( dXerciseScreen,
&DXMaxIOsPerDrive,
1L,
MAX_REQUESTS_PER_DISK,
10L,
0L,
TRUE,
3L,
"Number of concurrent IO to queue per drive [1-%d]? ",
MAX_REQUESTS_PER_DISK);
/* Ask for update interval */
PromptForUnsignedNumber( dXerciseScreen,
&DXUpdateInterval,
1L,
60L,
10L,
0L,
TRUE,
5L,
"Screen update interval in seconds [1-60]? ");
ConvertSecondsToTicks( DXUpdateInterval, 0L, &UpdateTicks );
/* List all possible disks */
ScanForNewDrives();
/* Ask for selections or go */
DriveCount = 0;
for (driveID = PhysicalDiskList; driveID != NULL; driveID =
driveID->PPLink)
{
for ( i = 0; i < sizeof( Disk[0].Name ); i++ ) Disk[DriveCount].Name[i] = 0;
MakeDriveDescription( driveID, Disk[DriveCount].Name );
LStatus = PromptForYesOrNo( dXerciseScreen,
0L,
TRUE,
"\r\nExercise %s ?",
Disk[DriveCount].Name );
if ( LStatus )
{
for ( lDisk = driveID->PLogicalLink;
lDisk != NULL;
lDisk = lDisk->LLogicalLink )
if ( lDisk->LOSType == NETWAREOSTYPE )
break;
if ( lDisk == NULL )
{
OutputToScreen( dXerciseScreen,
"\r\nDisk does not have a NetWare Partition - not usable\r\n");
break;
}
Disk[DriveCount].Lock = LockSinglePartition(
lDisk,
LockAlert,
DriveCount,
EXCLUSIVELOCK,
(BYTE *)"DXercise" );
if ( Disk[DriveCount].Lock == NULL )
{
OutputToScreen( dXerciseScreen,
"\r\nCannot get EXCLUSIVE LOCK on NetWare partition - not usable\r\n");
break;
}
Disk[DriveCount].LDisk = lDisk;
Disk[DriveCount].Size = lDisk->LLogicalSize;
Disk[DriveCount].OutstandingIOs = 0;
Performance[DriveCount].TotalIOsCompleted = 0;
Performance[DriveCount].CurrentIOsCompleted = 0;
Performance[DriveCount].TotalErrors = 0;
DriveCount++;
}
}
/* Zero aggregate preformance numbers */
Aggregate.TotalIOsCompleted = 0;
Aggregate.CurrentIOsCompleted = 0;
Aggregate.TotalErrors = 0;
AggregateOutstandingIOs = 0;
/* Make sure at least one disk is selected */
if ( DriveCount < 1 )
{
OutputToScreen( dXerciseScreen,
"No Drives selected\r\n" );
Delay( 91 );
DXExit();
}
/* Allocate and initialize the request list */
DXRequestAreaSize = DriveCount
* MAX_REQUESTS_PER_DISK
* ( sizeof( struct rstruct ) + 4 );
DXRequestArea = GetNonMovableMemory( DXRequestAreaSize,
&DXRequestAreaSize );
if ( DXRequestArea == NULL)
{
OutputToScreen ( dXerciseScreen,
"Insufficient memory to allocate disk requests\r\n");
Delay( 91 );
DXExit();
}
CSetB( 0, DXRequestArea, DXRequestAreaSize );
DXRequestFreeListHead = NULL;
DXRequestFreeListTail = NULL;
Request = (struct rstruct *)DXRequestArea;
for ( i = 0; i < DXRequestAreaSize / ( sizeof( struct rstruct ) + 4 ); i++ )
{
*(LONG *)Request = 0x55514552; /* Put in the "REQU" block signature */
Request = (struct rstruct *)((LONG *)Request + 1); /* just before block */
DXReturnRequest( Request );
Request++;
}
/* Start DXercise */
DXStack = GetNonMovableMemory( STACK_SIZE,
&DXActualStackSize );
if ( DXStack == NULL)
{
OutputToScreen ( dXerciseScreen,
"Insufficient memory to start DX subprocess\r\n");
Delay( 91 );
DXExit();
}
DXerciseProcessID = CCreateProcess( DX_PRIORITY,
DXercise,
DXStack + DXActualStackSize,
DXActualStackSize,
"DXercise");
/* Compute pages of displays */
MaxPage = ( DriveCount / DISKS_PER_PAGE ) + 1;
CurrentPage = 1;
/* Clear the Screen and setup the title */
ClearScreen( dXerciseScreen );
PositionOutputCursor( dXerciseScreen, 0, 0 );
OutputToScreen( dXerciseScreen,
"NetFRAME Disk Test : %s %s %d KB/IO with %d IOs queued Page %d of %d",
AccessString,
IOTypeString,
DXKBytesPerIO,
DXMaxIOsPerDrive,
CurrentPage,
MaxPage );
/* Note the starting time for later computations */
StartTime = CurrentTime;
while (1)
{
/* Sleep for update interval */
IntervalStartTime = CurrentTime;
Delay( UpdateTicks );
/* Test for keyboard key */
if ( CheckKeyStatus( dXerciseScreen ) )
{
/* Get the key and process it */
GetKey( dXerciseScreen, &Dummy, &Answer, &Dummy, &Dummy, 0L );
if ( Answer == 'Q' || Answer =='q' )
{
PositionOutputCursor( dXerciseScreen, 24, 0 );
LStatus = PromptForYesOrNo( dXerciseScreen,
0L,
TRUE,
"Exit Disk Exerciser? " );
if ( LStatus )
{
DXMonitorStatus = 'P';
}
}
else
{
if ( Answer > '0' && Answer <= '9' )
{
i = Answer - '0';
if ( i <= MaxPage )
{
CurrentPage = i;
ClearScreen( dXerciseScreen );
PositionOutputCursor( dXerciseScreen, 0, 0 );
OutputToScreen( dXerciseScreen,
"NetFRAME Disk Test : %s %s %d KB/IO with %d IOs queued Page %d of %d",
AccessString,
IOTypeString,
DXKBytesPerIO,
DXMaxIOsPerDrive,
CurrentPage,
MaxPage );
}
else RingTheBell();
}
else RingTheBell();
}
}
/* Test for requested stop */
if ( DXMonitorStatus == 'P' )
{
while ( AggregateOutstandingIOs != 0 ) CRescheduleLast();
if ( DXerciseProcessID != 0 )
{
CDestroyProcess( DXerciseProcessID );
DXerciseProcessID = 0;
}
DXMonitorStatus = 'S'; /* Say we are stopped */
ReturnNonMovableMemory( DXStack );
DXStack = NULL;
DXExit();
}
/* Compute and display the numbers */
ConvertTicksToSeconds( ( CurrentTime - StartTime ),
&ElapsedSeconds,
&Tenths );
ConvertTicksToSeconds( ( CurrentTime - IntervalStartTime ),
&IntervalSeconds,
&Tenths );
PositionOutputCursor( dXerciseScreen, 1, 0 );
OutputToScreen( dXerciseScreen, "%-28.28s "," Disk" );
OutputToScreen( dXerciseScreen, "%-9s "," KB/sec." );
OutputToScreen( dXerciseScreen, "%-9s "," IOs/sec." );
OutputToScreen( dXerciseScreen, "%-9s ","Ave. KB/s" );
OutputToScreen( dXerciseScreen, "%-9s ","Total IOs" );
OutputToScreen( dXerciseScreen, "%-5s\r\n","Error" );
FirstDrive = ( CurrentPage - 1 ) * DISKS_PER_PAGE;
/* note - LastDrive is really last drive index (0 based) plus 1) */
if ( CurrentPage == MaxPage )
{
LastDrive = DriveCount;
}
else
{
LastDrive = CurrentPage * DISKS_PER_PAGE;
}
for ( DriveIndex = FirstDrive; DriveIndex < LastDrive; DriveIndex++ )
{
OutputToScreen( dXerciseScreen, "%-28.28s ", Disk[DriveIndex].Name );
OutputToScreen( dXerciseScreen, "%9d ", ( Performance[DriveIndex].CurrentIOsCompleted * DXKBytesPerIO ) / IntervalSeconds );
OutputToScreen( dXerciseScreen, "%9d ", Performance[DriveIndex].CurrentIOsCompleted / DXUpdateInterval );
OutputToScreen( dXerciseScreen, "%9d ", ( Performance[DriveIndex].TotalIOsCompleted * DXKBytesPerIO ) / ElapsedSeconds );
OutputToScreen( dXerciseScreen, "%9d ", Performance[DriveIndex].TotalIOsCompleted );
OutputToScreen( dXerciseScreen, "%5d ", Performance[DriveIndex].TotalErrors );
OutputToScreen( dXerciseScreen, "\r\n" );
}
/* Aggregate numbers */
OutputToScreen( dXerciseScreen, "\r\n" );
OutputToScreen( dXerciseScreen, "%-28.28s ", "Aggregate Disk Performance" );
OutputToScreen( dXerciseScreen, "%9d ", ( Aggregate.CurrentIOsCompleted * DXKBytesPerIO ) / IntervalSeconds );
OutputToScreen( dXerciseScreen, "%9d ", Aggregate.CurrentIOsCompleted / DXUpdateInterval );
OutputToScreen( dXerciseScreen, "%9d ", ( Aggregate.TotalIOsCompleted * DXKBytesPerIO ) / ElapsedSeconds );
OutputToScreen( dXerciseScreen,"%9d ", Aggregate.TotalIOsCompleted );
OutputToScreen( dXerciseScreen,"%5d ", Aggregate.TotalErrors );
OutputToScreen( dXerciseScreen, "\r\n" );
/* Utilization */
utilization = 100 - ((NumberOfPollingLoops * 100 +
(MaximumNumberOfPollingLoops >> 1)) / MaximumNumberOfPollingLoops);
OutputToScreen( dXerciseScreen, "Server utilization %6d%%\r\n", utilization);
/* Instructions */
OutputToScreen( dXerciseScreen, "Q to quit or page number to view\n\r" );
/* Zero current counts */
for ( DriveIndex = 0; DriveIndex < DriveCount; DriveIndex++ )
{
Performance[DriveIndex].CurrentIOsCompleted = 0;
}
Aggregate.CurrentIOsCompleted = 0;
}
}
void
DXercise()
{
struct rstruct *Request;
int i;
while (1)
{
if ( DXMonitorStatus != 'R' ) /* If not running go to sleep forever */
/* and wait to be destroyed */
{
while (1) CSleepUntilInterrupt();
}
for ( i = 0; i < DriveCount; i++ )
{
while ( Disk[i].OutstandingIOs < DXMaxIOsPerDrive )
{
/* Start another IO */
Disk[i].OutstandingIOs++;
switch ( DXAccessType )
{
case 'F':
Disk[i].Sector = 0;
break;
case 'S':
Disk[i].Sector += DXKBytesPerIO * 2;
if ( Disk[i].Sector + ( DXKBytesPerIO * 2 ) >=
Disk[i].Size ) Disk[i].Sector = 0;
break;
case 'R':
Disk[i].Sector = RandomSectors[Disk[i].Random] % ( Disk[i].Size - ( DXKBytesPerIO * 2 ) );
Disk[i].Random++;
if ( Disk[i].Random == RandomSectorsCount )
Disk[i].Random = 0;
break;
}
/* Build a physical I/O request */
Request = DXAllocateRequest();
Request->RReserved = (LONG)Disk[i].LDisk;
Request->RDiskID = Disk[i].LDisk->LPLink;
Request->RStartingSector = Disk[i].Sector +
Disk[i].LDisk->LPartitionOffset;
Request->RMirrorLink = Request;
Request->RRoutine = DXIOCompletion;
Request->REBXValue = i;
Request->RNumberOfSectors = DXKBytesPerIO * 2;
Request->RBufferAddress = CommonBuffer;
if ( DXReadWrite == 'W' )
Request->RFunction = 1;
else
Request->RFunction = 0;
PDiskRequest( Request );
AggregateOutstandingIOs++;
}
}
DXWakeRequested = 0;
CSleepUntilInterrupt();
}
}
void
LockAlert( LONG skipParameter, LONG criticality, int DriveIndex )
{
_UNUSED( skipParameter );
_UNUSED( criticality );
_UNUSED( DriveIndex );
}
void
DXIOCompletion( struct rstruct *currentRequest )
{
int i;
i = (int)currentRequest->REBXValue;
--Disk[i].OutstandingIOs;
--AggregateOutstandingIOs;
/* Update global aggregate counts */
Aggregate.TotalIOsCompleted++;
Aggregate.CurrentIOsCompleted++;
/* Update individual disk counts */
Performance[i].TotalIOsCompleted++;
Performance[i].CurrentIOsCompleted++;
/* Count errors */
if ( currentRequest-> RCompletionCode != 0 )
{
Performance[i].TotalErrors++;
Aggregate.TotalErrors++;
}
DXReturnRequest( currentRequest );
if ( !DXWakeRequested )
{
DXWakeRequested = -1;
CRescheduleFromInterrupt( DXerciseProcessID );
}
}
struct rstruct *DXAllocateRequest()
{
struct rstruct *Request;
if ( DXRequestFreeListHead == NULL ) Abend( "DX Out of request nodes");
Request = DXRequestFreeListHead;
DXRequestFreeListHead = Request->RLink;
return ( Request );
}
void
DXReturnRequest( struct rstruct *Request)
{
if ( DXRequestFreeListHead == NULL )
{
DXRequestFreeListHead = Request;
DXRequestFreeListTail = Request;
}
else
{
DXRequestFreeListTail->RLink = Request;
DXRequestFreeListTail = Request;
}
DXRequestFreeListTail->RLink = NULL;
}
void
MakeDriveDescription(
struct pdstruct *driveID,
BYTE *buffer)
{
while (*buffer != 0)
++buffer;
sprintf(buffer, (BYTE *)"%15.15s ", driveID->PDeviceName + 1);
while (*buffer != 0)
++buffer;
sprintf(buffer, (BYTE *)"%d-", driveID->PCardNumber);
while (*buffer != 0)
++buffer;
sprintf(buffer, (BYTE *)"%d-", driveID->PControllerNumber);
while (*buffer != 0)
++buffer;
sprintf(buffer, (BYTE *)"%d", driveID->PDriveNumber);
}